<?php
/**
 * Created by PhpStorm.
 * User: cc
 * Date: 2021/7/13
 * Time: 5:03 下午
 */

namespace app\aliyun\controller;

use AlibabaCloud\Client\AlibabaCloud;
use AlibabaCloud\Client\Exception\ClientException;
use AlibabaCloud\Client\Exception\ServerException;
use AlibabaCloud\Live\Live;
use app\common\cachemodel\AppConfig;
use think\Env;
use think\Log;

/**
 * @url https://next.api.aliyun.com/api/live/2016-11-01/AddLiveDomainPlayMapping
 * Class AliyunLive
 * @package app\aliyun\controller
 */
class AliyunLive
{
    protected $regionId = 'cn-beijing';
    protected $AccessKeyId = 'LTAI5tPi1GHBNCZoVa6VsTnn';
    protected $AccessKeySecret = 'aTiFkcIJ3pvwXhRBosFLk3OpUsK5KG';
    protected $pushUrl;
    protected $playUrl;
    protected $push_domain = 'push-live.goddessxzns.com';//推流域名
    protected $push_key = 'VXJlpWI71e3MPinq';//推流域名配置的鉴权Key
    protected $play_domain = 'pull-live.goddessxzns.com';//播放域名
    protected $play_key = 'JgQqrtOchmoajEuH';//播放域名配置的鉴权Key
    protected $expireTime = 86400;//控制台的访问控制 > URL鉴权生成,有效时间为1天

    /**
     * 初始化客户端
     * AliyunLive constructor.
     * @throws ClientException
     */
    public function __construct()
    {
        AlibabaCloud::accessKeyClient($this->AccessKeyId, $this->AccessKeySecret)->regionId($this->regionId)->asDefaultClient();
    }

    /**
     * @url https://help.aliyun.com/knowledge_detail/162269.html?spm=5176.2020520154.help.dexternal.64b26bc0MuPlY6
     * 创建直播 - 生成直播地址
     * @param $liveId
     * @param $liveRecordId
     * @return array
     */
    public function createLive($liveId, $liveRecordId)
    {
        //生成随机的AppName
        $hostName = Env::get('host.name');
        $appName = "zhunle_{$hostName}";
        //生成随机的StreamName
        $streamName = $liveId . '-' . $liveRecordId . '-' . md5(uniqid(microtime(true), true));
        //生成推流地址
        $this->pushUrl = $this->push_url($this->push_domain, $this->push_key, $this->expireTime, $appName, $streamName);
        //生成播流地址
        $this->playUrl = $this->play_url($this->play_domain, $this->play_key, $this->expireTime, $appName, $streamName);
        //生成多个清晰度的播流地址
        $pull_url_definition_list = $this->play_url_definition_list($this->play_domain, $this->play_key, $this->expireTime, $appName, $streamName);
        return [
            'push_url' => $this->pushUrl,
            'pull_url' => $this->playUrl,
            'pull_url_definition_list' => $pull_url_definition_list,
            'app_name' => $appName,
            'stream_name' => $streamName
        ];
    }

    /**
     * 生成推流地址
     * @param $push_domain
     * @param $push_key
     * @param $expireTime
     * @param $appName
     * @param $streamName
     * @return string|void
     */
    private function push_url($push_domain, $push_key, $expireTime, $appName, $streamName)
    {
        //未开启鉴权Key的情况下
        if ($push_key === '')
        {
            return 'rtmp://' . $push_domain . '/' . $appName . '/' . $streamName;
        }
        $rand = 0;
        $uid = 0;
        $timeStamp = time() + $expireTime;
        $sstring = '/' . $appName . '/' . $streamName . '-' . $timeStamp . "-{$rand}-{$uid}-" . $push_key;
        return 'rtmp://' . $push_domain . '/' . $appName . '/' . $streamName . '?auth_key=' . $timeStamp . "-{$rand}-{$uid}-" . md5($sstring);
    }

    /**
     * 生成播流地址
     * @param $play_domain
     * @param $play_key
     * @param $expireTime
     * @param $appName
     * @param $streamName
     * @return string[]
     */
    private function play_url($play_domain, $play_key, $expireTime, $appName, $streamName)
    {
        $template = Env::get('host.name') . '-RTS';
        //未开启鉴权Key的情况下
        if ($play_key === '')
        {
            $rtmp_play_url = 'rtmp://' . $play_domain . '/' . $appName . '/' . $streamName;
            $flv_play_url = 'http://' . $play_domain . '/' . $appName . '/' . $streamName . '.flv';
            $hls_play_url = 'http://' . $play_domain . '/' . $appName . '/' . $streamName . '.m3u8';
            $udp_app_url = $udp_h5_url = 'artc://' . $play_domain . '/' . $appName . '/' . $streamName;//即低延迟直播RTS使用地址，需提前开通RTS服务。
        } else
        {
            $rand = 0;
            $uid = 0;
            $timeStamp = time() + $expireTime;

            $rtmp_sstring = '/' . $appName . '/' . $streamName . '-' . $timeStamp . "-{$rand}-{$uid}-" . $play_key;
            $rtmp_play_url = 'rtmp://' . $play_domain . '/' . $appName . '/' . $streamName . '?auth_key=' . $timeStamp . "-{$rand}-{$uid}-" . md5($rtmp_sstring);

            $flv_sstring = '/' . $appName . '/' . $streamName . '.flv-' . $timeStamp . "-{$rand}-{$uid}-" . $play_key;
            $flv_play_url = 'http://' . $play_domain . '/' . $appName . '/' . $streamName . '.flv?auth_key=' . $timeStamp . "-{$rand}-{$uid}-" . md5($flv_sstring);

            $hls_sstring = '/' . $appName . '/' . $streamName . '.m3u8-' . $timeStamp . "-{$rand}-{$uid}-" . $play_key;
            $hls_play_url = 'https://' . $play_domain . '/' . $appName . '/' . $streamName . '.m3u8?auth_key=' . $timeStamp . "-{$rand}-{$uid}-" . md5($hls_sstring);

            $udp_app_sstring = '/' . $appName . '/' . $streamName . '-' . $timeStamp . "-{$rand}-{$uid}-" . $play_key;
            $udp_app_url = 'artc://' . $play_domain . '/' . $appName . '/' . $streamName . '?auth_key=' . $timeStamp . "-{$rand}-{$uid}-" . md5($udp_app_sstring);

            $udp_h5_sstring = '/' . $appName . '/' . $streamName . '_' . $template . '-' . $timeStamp . "-{$rand}-{$uid}-" . $play_key;
            $udp_h5_url = 'artc://' . $play_domain . '/' . $appName . '/' . $streamName . '_' . $template . '?auth_key=' . $timeStamp . "-{$rand}-{$uid}-" . md5($udp_h5_sstring);
        }

        return [
            'rtmp' => $rtmp_play_url,
            'flv' => $flv_play_url,
            'hls' => $hls_play_url,
            'udp_app_url' => $udp_app_url,
            'udp_h5_url' => $udp_h5_url,
        ];
    }


    /**
     * 生成多个清晰度的播流地址
     * @param $play_domain
     * @param $play_key
     * @param $expireTime
     * @param $appName
     * @param $streamName
     * @return string[]
     */
    private function play_url_definition_list($play_domain, $play_key, $expireTime, $appName, $streamName)
    {
        $pull_url_definition_list = [];//转换结果

        try
        {
            $config = (new AppConfig())->configInfo('live');
            $live_definition_list_conf = json_decode(htmlspecialchars_decode($config['live_definition_list']), true);
            if (empty($live_definition_list_conf) || !is_array($live_definition_list_conf))
            {
                Log::write("AppConf配置的live_definition_list参数异常！", 'error', true);
                return $pull_url_definition_list;
            }

            foreach ($live_definition_list_conf as $v)
            {
                $template = $v['temp'];
                //未开启鉴权Key的情况下
                if ($play_key === '')
                {
                    $rtmp_play_url = 'rtmp://' . $play_domain . '/' . $appName . '/' . $streamName;
                    $flv_play_url = 'http://' . $play_domain . '/' . $appName . '/' . $streamName . '.flv';
                    $hls_play_url = 'http://' . $play_domain . '/' . $appName . '/' . $streamName . '.m3u8';
                    $udp_app_url = $udp_h5_url = 'artc://' . $play_domain . '/' . $appName . '/' . $streamName;//即低延迟直播RTS使用地址，需提前开通RTS服务。
                } else
                {
                    $rand = 0;
                    $uid = 0;
                    $timeStamp = time() + $expireTime;

                    $rtmp_sstring = '/' . $appName . '/' . $streamName . '-' . $timeStamp . "-{$rand}-{$uid}-" . $play_key;
                    $rtmp_play_url = 'rtmp://' . $play_domain . '/' . $appName . '/' . $streamName . '?auth_key=' . $timeStamp . "-{$rand}-{$uid}-" . md5($rtmp_sstring);

                    $flv_sstring = '/' . $appName . '/' . $streamName . '.flv-' . $timeStamp . "-{$rand}-{$uid}-" . $play_key;
                    $flv_play_url = 'http://' . $play_domain . '/' . $appName . '/' . $streamName . '.flv?auth_key=' . $timeStamp . "-{$rand}-{$uid}-" . md5($flv_sstring);

                    $hls_sstring = '/' . $appName . '/' . $streamName . '_' . $template . '.m3u8-' . $timeStamp . "-{$rand}-{$uid}-" . $play_key;
                    $hls_play_url = 'https://' . $play_domain . '/' . $appName . '/' . $streamName . '_' . $template . '.m3u8?auth_key=' . $timeStamp . "-{$rand}-{$uid}-" . md5($hls_sstring);

                    $udp_app_sstring = '/' . $appName . '/' . $streamName . '_' . $template . '-' . $timeStamp . "-{$rand}-{$uid}-" . $play_key;
                    $udp_app_url = 'artc://' . $play_domain . '/' . $appName . '/' . $streamName . '_' . $template . '?auth_key=' . $timeStamp . "-{$rand}-{$uid}-" . md5($udp_app_sstring);

                    $udp_h5_sstring = '/' . $appName . '/' . $streamName . '_' . $template . '-' . $timeStamp . "-{$rand}-{$uid}-" . $play_key;
                    $udp_h5_url = 'artc://' . $play_domain . '/' . $appName . '/' . $streamName . '_' . $template . '?auth_key=' . $timeStamp . "-{$rand}-{$uid}-" . md5($udp_h5_sstring);
                }

                $pull_url_definition_list[] = [
                    'rtmp' => $rtmp_play_url,
                    'flv' => $flv_play_url,
                    'hls' => $hls_play_url,
                    'udp_app_url' => $udp_app_url,
                    'udp_h5_url' => $udp_h5_url,
                ];
            }
        }
        catch (\Exception $e)
        {
            Log::write("生成多个清晰度的播流地址异常：{$e->getMessage()};File:{$e->getFile()};Line:{$e->getLine()}", 'error', true);
        }

        return $pull_url_definition_list;

    }

    /**
     * 开始或恢复直播
     * @param $appName
     * @param $streamName
     * @return array|string
     */
    public function resumeLive($appName, $streamName)
    {
        try
        {
            $request = Live::v20161101()->resumeLiveStream();
            $result = $request
                ->withDomainName($this->push_domain)
                ->withLiveStreamType("publisher")
                ->withAppName($appName)
                ->withStreamName($streamName)
                ->debug(false) // Enable the debug will output detailed information
                ->connectTimeout(1) // Throw an exception when Connection timeout
                ->timeout(1) // Throw an exception when timeout
                ->request();
            return $result->toArray();
        }
        catch (ClientException $exception)
        {
            return $exception->getMessage() . PHP_EOL;
        }
        catch (ServerException $exception)
        {
            echo $exception->getMessage() . PHP_EOL;
            echo $exception->getErrorCode() . PHP_EOL;
            echo $exception->getRequestId() . PHP_EOL;
            return $exception->getErrorMessage() . PHP_EOL;
        }
    }

    /**
     * 停止直播
     * @param $appName
     * @param $streamName
     * @return array|string
     */
    public function stopLive($appName, $streamName)
    {
        try
        {
            $request = Live::v20161101()->forbidLiveStream();
            $result = $request
                ->withDomainName($this->push_domain)
                ->withAppName($appName)
                ->withStreamName($streamName)
                ->withLiveStreamType("publisher")//用于指定主播推流还是客户端播流
                ->debug(false) // Enable the debug will output detailed information
                ->connectTimeout(1) // Throw an exception when Connection timeout
                ->timeout(1) // Throw an exception when timeout
                ->request();
            return $result->toArray();
        }
        catch (ClientException $exception)
        {
            echo $exception->getMessage() . PHP_EOL;
        }
        catch (ServerException $exception)
        {
            echo $exception->getMessage() . PHP_EOL;
            echo $exception->getErrorCode() . PHP_EOL;
            echo $exception->getRequestId() . PHP_EOL;
            return $exception->getErrorMessage() . PHP_EOL;
        }
    }

    /**
     * 获取直播在线人数 //todo 无权限
     * @param $appName
     * @param $streamNma
     * @param string $startTime date('Y-m-d\TH:i:s\Z', 1626246625)
     * @param string $endTime
     * @return array|string
     */
    public function getOnlineUserNum($appName, $streamNma, $startTime, $endTime)
    {
        try
        {
            $request = Live::v20161101()->describeLiveStreamHistoryUserNum();
            $result = $request
                ->withDomainName($this->push_domain)
                ->withAppName($appName)
                ->withStreamName($streamNma)
                ->withStartTime($startTime)
                ->withEndTime($endTime)
                ->debug(true) // Enable the debug will output detailed information
                ->connectTimeout(1) // Throw an exception when Connection timeout
                ->timeout(1) // Throw an exception when timeout
                ->request();
            return $result->toArray();
        }
        catch (ClientException $exception)
        {
            echo $exception->getMessage() . PHP_EOL;
        }
        catch (ServerException $exception)
        {
            echo $exception->getMessage() . PHP_EOL;
            echo $exception->getErrorCode() . PHP_EOL;
            echo $exception->getRequestId() . PHP_EOL;
            return $exception->getErrorMessage() . PHP_EOL;
        }
    }
}
